# encoding=utf-8
# MVVM架构中的视图模型，通过数据绑定自动更新视图或进行业务逻辑处理,同时进行view与model间的数据转换
# BingoLee 2024-04-17
from collections import defaultdict
from typing import Any, Final

from utils.kit_utils import Log
# from model.kit_data import cache


class Topic:
    """ 数据驱动界面变更的topic """

    # =======================================================
    #                     View视图处理
    # =======================================================

    # 显示消息窗体数据事件，数据格式：list: [level, message, wait]
    SHOW_MESSAGE_BOX: Final = 'SHOW_MESSAGE_BOX'

    # 显示新APP版本release note事件，数据格式：dict
    SHOW_RELEASE_NOTE: Final = 'SHOW_RELEASE_NOTE'

    # 用户首选项配置数据变更事件，数据格式：dict
    SETTINGS_DATA_CHANGED: Final = 'SETTINGS_DATA_CHANGED'

    # 用户自定义AT指令集数据变更事件，数据格式：dict
    CUSTOM_AT_CMDS_CHANGED: Final = 'CUSTOM_AT_CMDS_CHANGED'

    # 项目定义的AT指令集数据变更事件，数据格式：dict
    PROJECT_AT_CMDS_CHANGED: Final = 'PROJECT_AT_CMDS_CHANGED'

    # COM端口变更事件，数据格式：list
    COMPORTS_DATA_CHANGED: Final = 'COMPORTS_DATA_CHANGED'

    # 串口数据接收窗数据更新，数据格式：str
    AT_CMD_RECV_DATA_CHANGED: Final = 'AT_CMD_RECV_DATA_CHANGED'

    # =======================================================
    #                     Model模块处理
    # =======================================================

    # 开始自动升级APP，数据格式：None
    APP_UPGRADE_START: Final = 'APP_UPGRADE_START'

    # 发送AT指令，数据格式：str
    SEND_AT_CMD_DATA: Final = 'SEND_AT_CMD_DATA'

    # 添加自定义AT指令数据，数据格式：tuple  (tab, name, value)
    ADD_CUSTOM_AT_CMD_DATA: Final = 'ADD_CUSTOM_AT_CMD_DATA'

    # 修改自定义AT指令数据，数据格式：tuple  ((tab, old_name, old_value), (tab, new_name, new_value))
    MOD_CUSTOM_AT_CMD_DATA: Final = 'MOD_CUSTOM_AT_CMD_DATA'

    # 修改自定义AT指令标签名，数据格式：tuple  (old_tab, new_tab)
    MOD_CUSTOM_AT_CMD_TAB: Final = 'MOD_CUSTOM_AT_CMD_DATA'

    # 删除自定义AT指令数据，数据格式：tuple  (tab, name, value)
    DEL_CUSTOM_AT_CMD_DATA: Final = 'DEL_CUSTOM_AT_CMD_DATA'

    # 删除自定义AT指令标签及其子数据，数据格式：str
    DEL_CUSTOM_AT_CMD_TAB: Final = 'DEL_CUSTOM_AT_CMD_TAB'


class TopicBinder:
    """
    视图数据绑定类，当绑定的Topic数据变更时，将自动触发回调
    使用观察者模式，提供一对一及一对多绑定

    场景一：
        View视图调用bind()以及unbind()绑定/取消绑定数据变更处理的回调函数
        Model模块调用notify()进行回调函数调用
        从而实现model数据变更驱动view视图更新

    场景二：
        Model模块调用bind()以及unbind()绑定/取消绑定数据变更处理的回调函数
        View视图调用notify()进行回调函数调用
        从而实现view视图更新驱动model进行业务逻辑等处理
    """
    _bind_funcs = defaultdict(set)

    @classmethod
    def bind(cls, topic: str, handler) -> None:
        cls._bind_funcs[topic].add(handler)

    @classmethod
    def unbind(cls, topic: str, handler) -> None:
        if topic in cls._bind_funcs and handler in cls._bind_funcs[topic]:
            cls._bind_funcs[topic].remove(handler)

    @classmethod
    def notify(cls, topic: str, data: Any = None):
        if topic in cls._bind_funcs:
            real_data = DataTranslater.trans(topic, data)
            for handler in cls._bind_funcs[topic]:
                handler(real_data)
        else:
            Log.warn(f"{topic} not bind callback function yet, plz check!")


class DataTranslater:
    """
    数据转换层
    view/model在notify操作时，只需要提供自身原始数据即可，具体的数据转换在viewmodel中进行
    """

    @classmethod
    def trans(cls, topic: str, data: Any = None):
        func = {
            Topic.SETTINGS_DATA_CHANGED: cls. model_to_view_settings_data,
            Topic.PROJECT_AT_CMDS_CHANGED: cls.model_to_view_project_at_cmds,
            Topic.CUSTOM_AT_CMDS_CHANGED: cls.model_to_view_custom_at_cmds,
        }.get(topic, cls.default)
        return func(data)

    @classmethod
    def default(cls, in_data: Any = None):
        return in_data

    @classmethod
    def model_to_view_settings_data(cls, in_data):
        return {
            'AT_DFT_BAUDRATE': in_data.get('at_default_baudrate', "115200"),
            'DEFAULT_THEME': in_data.get('default_theme', 'Light'),
            'CUSTOM_AT_CMDS': in_data.get('custom_at_cmds', {})
        }

    @classmethod
    def view_to_model_settings_data(cls, in_data):
        return in_data

    @classmethod
    def model_to_view_project_at_cmds(cls, in_data):
        out_data = {}
        for project, values in in_data.items():
            _values = {}
            for tab_name, item in values.items():
                _values[f'  {tab_name.strip()}  '] = item
            out_data[project] = _values
        return out_data

    @classmethod
    def model_to_view_custom_at_cmds(cls, in_data):
        tabs = []
        for tab, values in in_data.items():
            node, subs = (tab, ''), []
            for key, value in values.items():
                subs.append(((key, value), []))
            tabs.append((node, subs))
        return {'  自定义  ': tabs}
